package edu.pitt.cs3530.scripts;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Map.Entry;

public class CombineQos {
	public static void main(String args[]) {
		File directory = new File(args[0]);
		File[] fileArray = directory.listFiles();
		Map<Integer, List<File>> fileMap = new HashMap<Integer, List<File>>();
		List<File> list = null;
		int lastTest = 0;
		for (int i = 0; i < fileArray.length; i++) {
			StringTokenizer tok = new StringTokenizer(fileArray[i]
					.getName(), "-");
			tok.nextToken();
			int test = Integer.parseInt(tok.nextToken());
			int run = Integer.parseInt(tok.nextToken());
			if (test != lastTest) {
				lastTest = test;
				list = new ArrayList<File>();
				fileMap.put(test, list);
			}
			list.add(new File(fileArray[i].getAbsolutePath()));
			System.out.println("File found: " + fileArray[i].getAbsolutePath());
		}
		for (Entry<Integer, List<File>> entry : fileMap.entrySet()) {
			try {
				long currentTime = System.currentTimeMillis();
				System.out.println(directory.getAbsolutePath() + entry.getKey());
				BufferedWriter writer = new BufferedWriter(new FileWriter(directory.getAbsolutePath() + "/" +
						+ entry.getKey()));
				List<String> toWrite = new ArrayList<String>();
				System.out.println("Processing: " + entry.getKey());
				for (File file : entry.getValue()) {
					System.out.println("reading: " + file.getAbsolutePath());
					List<String> temp = readFile(file);
					System.out.println("normalizing: " + file.getAbsolutePath());
					normalizeStrings(temp);
					System.out.println("sorting: " + file.getAbsolutePath());
					sortWithHash(temp);
					
					System.out.println("merging: " + file.getAbsolutePath());
					System.out.println(toWrite.size() + temp.size());
					toWrite = merge(toWrite, temp);
					System.out.println(toWrite.size());
					System.out.println("finished: " + file.getAbsolutePath());
				}
				for (String string : toWrite) {
					writer.write(string);
					writer.write("\r\n");
				}
				writer.close();
				System.out.println("Total time: " + (System.currentTimeMillis() - currentTime));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	private static void normalizeStrings(List<String> list) {
		long min = Long.MAX_VALUE;
		for (String string : list) {
			StringTokenizer tok = new StringTokenizer(string, "-");
			tok.nextToken();
			long temp = Long.parseLong(tok.nextToken());
			if (temp < min) {
				min = temp;
			}
		}
		List<String> retval = new LinkedList<String>();
		for (String string : list) {
			StringTokenizer tok = new StringTokenizer(string, "-");
			String fin = tok.nextToken();
			long firstSec = Long.parseLong(tok.nextToken());
			long firstUsec = Long.parseLong(tok.nextToken());
			long secondSec = Long.parseLong(tok.nextToken());
			long secondUsec = Long.parseLong(tok.nextToken());
			firstSec = firstSec - min;
			secondSec = secondSec - min;
			retval.add(new String(fin + "-" + firstSec + "-" + firstUsec + "-" + secondSec
					+ "-" + secondUsec));
		}
		list.clear();
		list.addAll(retval);
	}

	private static List<String> readFile(File file) {
		List<String> retval = new ArrayList<String>();
		try {
			BufferedReader reader = new BufferedReader(new FileReader(file));
			while (reader.ready()) {
				String line = reader.readLine();
				if ((line != null) && (line.trim().length() != 0)) {
					retval.add(line);
				}
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return retval;
	}
	
	private static void sortWithHash(List<String> list) {
		Map<Long, List<String>> map = new HashMap<Long, List<String>>();
		List<Long> allTimes = new ArrayList<Long>();
		for (String string : list) {
			long time = getStartSeconds(string);
			if (!contains(allTimes, time)) {
				map.put(time, new ArrayList<String>());
				allTimes.add(time);
			}
			map.get(time).add(string);
		}
		list.clear();
		System.out.println("Put into hash");
		Collections.sort(allTimes);
		for (Long l : allTimes) {
			double percentage = (double) allTimes.indexOf(l) / (double) allTimes.size();
			System.out.println(percentage + " done sorting");
			List<String> times = map.get(l);
			sort(times);
			list.addAll(times);
		}
	}
	
	private static boolean contains(List<? extends Object> list, Object o) {
		for (Object object : list) {
			if (object.equals(o)) {
				return true;
			}
		}
		return false;
	}
	
	private static void sortNear(List<String> list) {
		List<String> temp = new ArrayList<String>();
		long currentTime = System.currentTimeMillis();
		while (!list.isEmpty()) {
			String min = list.get(0);
			long minValue = getTime(min);
			for (int i = 0; (i < 2000) && (i < list.size()); i++) {
				long tempValue = getTime(list.get(i));
				if (tempValue < minValue) {
					min = list.get(i);
					minValue = getTime(min);
				}
			}
			temp.add(min);
			list.remove(min);
			if (list.size() % 100 == 0) {
				System.out.println(list.size() + ":::" + (System.currentTimeMillis() - currentTime));
				currentTime = System.currentTimeMillis();
			}
		}
		list.addAll(temp);
	}

	private static void sort(List<String> list) {
		List<String> temp = new ArrayList<String>();
		List<String> sortList = new LinkedList<String>(list);
		while (!sortList.isEmpty()) {
			String min = sortList.get(0);
			long minValue = getTime(min);
			for (String string : sortList) {
				long tempValue = getTime(string);
				if (tempValue < minValue) {
					min = string;
					minValue = tempValue;
				}
			}
			temp.add(min);
			sortList.remove(min);
		}
		list.clear();
		list.addAll(temp);
	}

	private static List<String> merge(List<String> left, List<String> right) {
		List<String> retval = new ArrayList<String>();
		// Convert to LinkedList because ArrayLists have a truly horrendous
		// time penalty when removing an item from the list
		List<String> leftList = new LinkedList<String>(left);
		List<String> rightList = new LinkedList<String>(right);
		while (!leftList.isEmpty() && !rightList.isEmpty()) {
			StringTokenizer tok = new StringTokenizer(leftList.get(0), "-");
			tok.nextToken();
			long leftTime = getTime(leftList.get(0));
			tok = new StringTokenizer(rightList.get(0), "-");
			tok.nextToken();
			long rightTime = getTime(rightList.get(0));
			if (leftTime > rightTime) {
				retval.add(rightList.remove(0));
			} else {
				retval.add(leftList.remove(0));
			}
			if (leftList.isEmpty() || rightList.isEmpty()) {
				break;
			}
		}
		if (leftList.isEmpty()) {
			retval.addAll(rightList);
		}
		if (rightList.isEmpty()) {
			retval.addAll(leftList);
		}
		return retval;
	}

	private static long getTime(String time) {
		StringTokenizer tok = new StringTokenizer(time, "-");
		tok.nextToken();
		long second = Long.parseLong(tok.nextToken());
		long uSec = Long.parseLong(tok.nextToken());
		long retval = second * 1000000 + uSec;
		return retval;
	}
	
	private static long getStartSeconds(String time) {
		StringTokenizer tok = new StringTokenizer(time, "-");
		tok.nextToken();
		return Long.parseLong(tok.nextToken());
	}
}
